#include <debug.h>
#include <macro.h>
#include <stdarg.h>
#include <vsprintf.h>
#include <string.h>
#include <io.h>

/* SekiOS - debug.c
 *
 * serial debug support
 *
 * credits: Napalm
 */


void debug_setbaudrate(unsigned short base, int rate)
{
	/*
	  Base Addresses COM1:0x03F8 COM2:0x02F8
	  ======================================
	  Base     = Divisor Latch Low Byte  (DLL) when DLAB=1
	  Base + 1 = Divisor Latch High Byte (DLM) when DLAB=1
	  Base + 3 = Line Control Register (LCR)
	  0x80     = Bit 7 Divisor Latch Access Bit (DLAB).
		     1 = Set Latch Access, 0 = Enable Register Access.

	  Baud Rates Table (using standard 1.8432 Mhz clock)
	  ==================================================
	  BaudRate	Divisor		DLM  DLL
	  50		2304		0x09 0x00
	  75		1536		0x06 0x00
	  150		768		0x03 0x00
	  300		384		0x01 0x80
	  600		192		0x00 0xC0
	  1200		96		0x00 0x60
	  1800		64		0x00 0x40
	  2000		58		0x00 0x3A
	  2400		48		0x00 0x30
	  3600		32		0x00 0x20
	  4800		24		0x00 0x18
	  7200		16		0x00 0x10
	  9600		12		0x00 0x0C
	  14400		8		0x00 0x08
	  19200		6		0x00 0x06
	  38400		3		0x00 0x03
	  57600		2		0x00 0x02
	  115200	1		0x00 0x01
	*/
	unsigned short divisor = (115200 / rate);
	outportb(base + 3, inportb(base) |  0x80);	// Set DLAB = 1
	outportb(base,      (0x00FF & divisor));		// Set Divisor Low Byte
	outportb(base + 1, ((0xFF00 & divisor) >> 8));	// Set Divisor High Byte
	outportb(base + 3, inportb(base) & ~0x80);	// Set DLAB = 0
}

void debug_put(unsigned short base, unsigned char ch)	// Blocking
{
	/*
	  Base Addresses COM1:0x03F8 COM2:0x02F8
	  ======================================
	  Base + 5 = Line Status Register (LSR)
	  0x20     = Bit 5 Transmitter Holding Register Empty (THRE)
	             Controller is ready to accept a new character to send.
	*/
	while((inportb(base + 5) & 0x20)==0){	// Wait for ready state.
		idle_moment();				// Free CPU Time
	}
	outportb(base, ch);				// Out byte to port
}

void debug_puts(unsigned short base, char * str)
{
	while(*str != 0){				// While not null terminator
		if(*str == '\n')
			debug_put(base, '\r');
		debug_put(base, *str++);	// Output char
	}
}

int debug_printf(unsigned short base, char * format, ...)
{
	char buffer[1024];				// Allocate string buffer
	memset(buffer, 0, 1024);		// Zero the buffer
	va_list args; va_start(args, format);	// Setup arg list for vsprintf
	int r = vsprintf(buffer, format, args);	// r = Buffer used (XXX FIXME: Possible Overflow)
	debug_puts(base, buffer);		// Send buffer to serial port
	va_end(args);					// Clean up
	return r;						// return char count
}

int debug_get(unsigned short base)			// Non-blocking
{
	/*
	  Base Addresses COM1:0x03F8 COM2:0x02F8
	  ======================================
	  Base + 5 = Line Status Register (LSR)
	  0x1      = Bit 0 Data Ready.
	             A complete incoming character has been received and
		     sent to the receiver buffer register.
	  0x2      = Bit 1 Overrun Error (OE).
	             A character was sent to the receiver buffer before the
	             previous character in the buffer could be read.
	             This destroys the previous character.
	*/
	if((inportb(base + 5) & 0x1))	// Do we have data available?
		return inportb(base);		// Get char from port
	else	return -1;				// No data, return -1
}

int debug_gets(unsigned short base, char * buf, int max)
{
	int recv = 0, ch = 0;
	while(((ch = debug_get(base)) != 0)	// Wait for NULL terminator
	      && (recv < max)){			// Make sure we dont overflow
		if(ch != -1){				// Got char?
			*buf = (char)ch;		// Add char to the buffer;
			buf++; recv++;			// Increment buffer and Received
		}
		idle_moment();				// Free CPU Time
	}
	*buf = 0;						// Null terminate the buffer
	return recv;
}
